# 使用react
30 秒速记
- 这个项目是客户端渲染:
Koa返回一个空壳HTML,浏览器加载打包好的JS,由React渲染页面 - 链路:
src/apps写页面组件 →src/pages挂载入口 →Webpack编译到output/dist→ 模板用<script>引用 - 要服务端渲染(
SSR)就得在Koa里用renderToString出HTML,客户端再hydrate,两边代码版本必须一致 - 把服务端数据塞进
<script>时要转义<,否则一个</script>就能注入脚本 React 18起挂载用createRoot,ReactDOM.render在React 19被移除
在 Koa 项目里用 React,最常见的就是客户端渲染:服务端只给一个带挂载节点的页面,真正的界面由浏览器里的 React 画出来。 具体链路是 src/apps 写页面组件,src/pages 里调用渲染方法挂到 #app 上,Webpack 把它们编译到 output/dist,Koa 的模板再用 <script> 引进来。它的缺点是首屏要等 JS 下载执行完,对 SEO 也不友好,要解决就得上 SSR,那时候服务端和客户端的构建版本必须对上,不然 hydrate 会报不匹配。
理解「使用react」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。
工程中还要验证退出码、资源清理和异常输入。属于 module 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。
在 Koa 里用 React,先要分清是哪种模式:
- 客户端渲染:
Koa只返回一个带空节点的HTML,浏览器下载打包好的JS后由React生成界面。这个项目就是这种。 - 服务端渲染(
SSR):Koa里用react-dom/server的renderToString先生成完整HTML返回,浏览器拿到就有内容,JS加载后再hydrate接管交互。 - 静态生成:构建时就把页面渲染成
HTML文件,Koa只当静态服务器。
客户端渲染的整条链路:
// src/apps/index.jsx:只定义页面长什么样
export default function App() { return <h1>index</h1> }
// src/pages/index.js:Webpack 入口,负责挂载
import { createRoot } from 'react-dom/client' // React 18+
import App from '../apps/index.jsx'
createRoot(document.getElementById('app')).render(<App />)
<!-- Koa 返回的模板 -->
<div id="app"></div>
<script src="/output/dist/vendorjs/vendor.js"></script>
<script src="/output/dist/vendorjs/index.js"></script>
<!-- 浏览器执行后,#app 里出现 <h1>index</h1> -->
版本差异要说清:React 17 及以前用 ReactDOM.render(<App />, el);React 18 改成 createRoot(el).render(<App />),旧写法还能用但会警告,并且退回到不支持并发特性的旧模式;React 19 已经把 ReactDOM.render 移除了。如果以后要上 SSR,服务端往页面里注入初始数据时要转义 <,防止 </script> 注入。
面试官追问
追问 1页面空白,构建命令退出码是 0,怎么继续查?
退出码 0 只说明编译没报错。打开 DevTools 看三处:Console 有没有运行时报错、Network 里脚本是不是 404 或返回了 HTML、Elements 里挂载节点存不存在。
追问 2客户端渲染和服务端渲染,怎么选?
后台管理、登录后才能看的页面,客户端渲染就够了,简单。要 SEO 或者很在意首屏速度的公开页面,比如文章详情、商品页,才值得上 SSR,代价是服务端要跑 React、多一套构建。
追问 3服务端把用户信息用 JSON.stringify 写进 <script> 标签,有什么风险?
如果数据里有 </script><script>alert(1)</script>,浏览器会提前结束标签执行后面的脚本。要把 < 转义成 \u003c 再输出,或者用 serialize-javascript,也别把手机号、token 这类敏感字段带进去。
追问 4做 SSR 时控制台报 hydration 不匹配,常见原因是什么?
服务端和客户端渲染出的内容不一样:比如组件里直接用了 Date.now()、Math.random(),或者读了 window,又或者服务端和客户端用的不是同一版构建。会变的东西放到 useEffect 里在客户端再算。
# 使用react.js
# react.js简介
30 秒速记
React是做视图的库,不是全家桶框架:路由、请求、状态管理要自己选- 核心思想:界面 = 函数(状态),状态变了
React重新算界面,你不用手动改DOM JSX是React.createElement的语法糖,要编译后才能在浏览器跑- 虚拟
DOM让React算出最小改动再更新真实DOM,它的价值是声明式写法,不是「一定更快」 - 配套常见选型:路由
react-router、状态zustand/redux,要整套方案可以直接用Next.js
React 是一个专门负责视图渲染的 JavaScript 库,不是包办一切的框架。 它的核心思路是你只描述「数据是这样时界面长什么样」,数据变了 React 自己算出该怎么改 DOM。界面用 JSX 写,它看着像 HTML,其实会被编译成 React.createElement 调用,生成虚拟 DOM 对象。正因为它只管视图,路由、数据请求、目录规范都要自己搭配,想要开箱即用的整套方案,现在一般会选 Next.js 这类基于 React 的框架。
react.js 是作为前端渲染的js库(注意:不是框架)。react.js用JSX开发来描述DOM结构,通过编译成virtual dom的在浏览器中进行view渲染和动态交互处理。更多了解可查阅GitHubhttps://facebook.github.io/react/
面试官追问
追问 1JSX 编译后是什么样子?
<div className="a">hi</div> 会变成 React.createElement('div', { className: 'a' }, 'hi'),返回一个描述节点的普通对象。React 17 起的新 JSX 转换会改用 react/jsx-runtime 的 jsx(),所以文件里不再必须 import React。
追问 2筛选条件变了,数据也拿到了,列表就是不刷新,常见原因?
数据存在普通变量里,或者直接改了 state 对象,比如 list.push(x) 后 setList(list),引用没变 React 认为没变化。要用 setList([...list, x]) 这种生成新引用的写法。
追问 3虚拟 DOM 一定比直接操作 DOM 快吗?
不一定。手写一次精准的 DOM 操作肯定比「生成虚拟 DOM、对比、再更新」快。虚拟 DOM 的价值是让你用声明式写法也能有不错的性能,代码好维护。
追问 4一个几乎没交互的资讯详情页,要不要改成 React?
一般不值得。纯展示页面用服务端模板输出 HTML 就够了,首屏更快、SEO 更好。有大量状态变化和交互、组件复用多的页面,React 才划算。
# 编译使用
30 秒速记
- 浏览器不认识
JSX,必须编译:<App />→React.createElement(App) - 分工:
Babel(@babel/preset-react)翻译语法,Webpack组织依赖、打包输出 - 页面引用的是
output/dist里的产物,引用src里的.jsx必然报Unexpected token '<' Unexpected token '<'还有一种情况:脚本地址404被回退成了HTML页面- 学习时可以用
@babel/standalone在浏览器里实时编译,生产不能这么干
JSX 浏览器直接跑不了,所以开发和运行之间一定有一步编译,最常用的是 Webpack 加 Babel。 Babel 负责把 JSX 翻译成 React.createElement 调用,顺便把新语法降级;Webpack 从入口出发把组件、第三方库打包到一起,输出到 output/dist。页面上引用的必须是编译后的文件。排查时我见得最多的报错是 Unexpected token '<',要么是引用了源码,要么是脚本地址 404 了,服务端返回了一个 HTML 页面。
